package com.zhjhserver.socket;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import net.sf.json.JSONObject;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.future.CloseFuture;
import org.apache.mina.core.future.IoFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

import com.zhjhserver.database.*;
import com.zhjhserver.utils.*;

public class ServerHandler extends IoHandlerAdapter
{
    HashMap<IoSession, Player> mPlayerSessionMap = null;
    HashMap<String, Player> mPlayerNameMap = null;
    Hall mHall = null;

	public ServerHandler(Map<Long, IoSession> managedSessions) {
		System.out.println("服务器启动");

		mPlayerSessionMap = new HashMap<IoSession, Player>();
		mPlayerNameMap = new HashMap<String, Player>();
		mHall = new Hall();
	}

	/*
	 * 当接口中其他方法抛出异常未被捕获时触发此方法
	 */
	@Override
	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		System.out.println("当接口中其他方法抛出异常未被捕获时触发:" + cause.getMessage());
	}

	/*
	 * 当信息已经传送给客户端后触发此方法.
	 */
	@Override
	public void messageSent(IoSession session, Object message) throws Exception {
		System.out.println("当信息已经传送给客户端后触发:"+session.getRemoteAddress());
	}

	/*
	 * 当一个新客户端连接后触发此方法.
	 */
	@Override
	public void sessionCreated(IoSession session) throws Exception {
		System.out.println("当一个新客户端连接后触发:"+session.getRemoteAddress());
	}

	/*
	 * 当连接空闲时触发此方法.
	 */
	@Override
	public void sessionIdle(IoSession session, IdleStatus status)
			throws Exception {
		System.out.println("当连接空闲时触发:"+session.getRemoteAddress());
	}

	/*
	 * 当连接后打开时触发此方法，一般此方法与 sessionCreated 会被同时触发
	 */
	@Override
	public void sessionOpened(IoSession session) throws Exception {
		System.out.println("客户端上线:" + session.getRemoteAddress());
	}

	/*
	 * 当连接被关闭时触发，例如客户端程序意外退出等等.
	 */
	@Override
	public void sessionClosed(IoSession session) throws Exception {

		CloseFuture closeFuture = session.close(true);
		closeFuture.addListener(new IoFutureListener<IoFuture>() {
			public void operationComplete(IoFuture future) {
				if (future instanceof CloseFuture) {
					((CloseFuture) future).setClosed();
					IoSession session = future.getSession();
					System.out.println("客户端下线:"
							+ session.getRemoteAddress());
				    Player player = (Player)mPlayerSessionMap.get(session);
				    // 通知同桌其他玩家有玩家退出
				    mHall.notifySameTable(player, Const.FUNC_OTHER_PLAYER_EXIT);

				    String username = player.getUsername();
				    mPlayerSessionMap.remove(session);
				    mPlayerNameMap.remove(username);

			    	player.logout();
				}
			}
		});

	}

	/*
	 * 当接收到客户端的请求信息后触发此方法
	 */
	@Override
	public void messageReceived(IoSession session, Object message)
			throws Exception {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		String datetime = sdf.format(new Date());
		System.out.println("收到客户端数据("+session.getRemoteAddress()+")：" + datetime);
		PackData msg = (PackData) message;
		//DataEncyt encyt = new DataEncyt();
		IoBuffer ib = msg.getIoBuffer();
		//ib.position(ib.remaining());
		//IoBuffer buf = encyt.encryption(ib);
		//IoBuffer buf = ib;
		//buf.position(0);

		PackData pkg = procRecvData(session, ib);

		if (pkg != null)
		{
			session.write(pkg);
		}

		//PackageData pkg = new PackageData(buf);
		//System.out.println("ServerHandler.messageReceived()"+pkg.readInt());
		//session.write(msg);
	}

	private PackData procRecvData(IoSession session, IoBuffer buf)
	{
	    int versionCode = buf.getInt();
		int funcId = buf.getInt();
		byte[] b = new byte[buf.remaining()];
		buf.get(b);

		String jsonText = new String(b);
		String jsonAck = null;
		Player player = (Player)mPlayerSessionMap.get(session);

		if (funcId!=Const.FUNC_LOGON && player==null)
		{
		    return null;
		}

		switch (funcId)
		{
		    case Const.FUNC_LOGON:
		    	jsonAck = userLogon(session, jsonText);
		    	break;

		    case Const.FUNC_FAST_FIND_SEAT:
		    	jsonAck = mHall.fastFindSeat(player);
		    	mHall.notifySameTable(player, Const.FUNC_OTHER_PLAYER_ADD);
		    	break;

            case Const.FUNC_READY_PLAY:
                player.ready();
                mHall.notifySameTable(player, Const.FUNC_OTHER_PLAYER_READY);
                break;

		    default:
		        break;
		}

		if (jsonAck != null)
		{
			byte[] data = jsonAck.getBytes();

			int len = Const.FUNC_ID_LEN + data.length;
			PackData pkg = new PackData(len);
			pkg.writeInt(funcId);
			pkg.writeBytes(data);

			return pkg;
		}

		return null;
	}

	private String userLogon(IoSession session, String jsonText)
	{
		int error = Const.ERROR_UNKOWN;
		String info = null;
		boolean isAutoMallocName = false;

		JSONObject jsonIn = JSONObject.fromObject(jsonText);
		String username = JSONUtil.getString(jsonIn, "username");
		String passwd = JSONUtil.getString(jsonIn, "passwd");
		String deviceID = JSONUtil.getString(jsonIn, "device_id");
		String deviceModel = JSONUtil.getString(jsonIn, "device_model");

		if (PubFunc.isEmptyString(username))
		{
			// 给此用户分配一个帐号或找到之前的帐号
			JSONObject jsonAuto = UserDb.autoMallocAccount(deviceID, deviceModel);
			error = JSONUtil.getInt(jsonAuto, "error");
			username = JSONUtil.getString(jsonAuto, "username");
			passwd = JSONUtil.getString(jsonAuto, "passwd");
			isAutoMallocName = true;

			if (error != Const.ERROR_SUCCESS)
			{
			    JSONObject jsonOut = new JSONObject();
		        jsonOut.put("error", error);
		        return jsonOut.toString();
			}
		}

		if (UserDb.exists(username))
		{
		    if (mPlayerNameMap.containsKey(username) || mPlayerSessionMap.containsKey(session))
		    {
		        // 用户已登录了，不能重复登录
		        error = Const.ERROR_MUL_LOGON;
		    }
		    else
		    {
		        Player player = new Player(session, username);
		        mPlayerSessionMap.put(session, player);
		        mPlayerNameMap.put(username, player);
    			//money = player.getMoney();
    			info = player.getInfo();
    			player.logon();

    			error = Const.ERROR_SUCCESS;
    		}
		}
		else
		{
			error = Const.ERROR_INVALID_NAME;
		}

		JSONObject jsonOut = new JSONObject();
		jsonOut.put("error", error);
		jsonOut.put("info", info);

		if (isAutoMallocName)
		{
			jsonOut.put("username", username);
			jsonOut.put("passwd", passwd);
		}

		return jsonOut.toString();
	}
}
